package loan.calc.calculator.instalment.impl;

import loan.calc.calculator.instalment.InstalmentCalculatorParamDTO;
import loan.calc.constant.dict.RepayMethodDictEnum;
import loan.calc.util.BigDecimalUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

import static loan.calc.util.BigDecimalUtils.DEFAULT_CURRENCY_ROUNDING_MODE;

/**
 * 等额本息计算器
 */
@Component
public class InstalmentEqualCalculatorImpl extends AbstractInstalmentCalculator {

    @Override
    public RepayMethodDictEnum support() {
        return RepayMethodDictEnum.EQ_PI;
    }

    @Override
    public BigDecimal calcInstalmentAmount(InstalmentCalculatorParamDTO param) {
        Integer totalPeriods = param.getTotalPeriods();
        BigDecimal totalPrincipalAmount = param.getTotalPrincipalAmount();
        BigDecimal periodRate = param.getPeriodRate();
        BigDecimal instalment = BigDecimal.ZERO;
        if (totalPeriods == 0) {
            // 特殊情况，如果期数为0，则期供为0。应该不会出现，因为正常rpt，最少剩余期数也为1
            param.setInstalmentAmt(BigDecimal.ZERO);
            return instalment;
        }
        if (periodRate.compareTo(BigDecimal.ZERO) > 0) {
            //等额本息的公式  期供金额 = [总本金×期利率×(1+期利率)^总期数] ÷ [(1+期利率)^总期数-1]
            //换算后的公式 （还没懂怎么算的，先不用）   期供金额=贷款本金×期次利率×(1+1÷[(1+期次利率)^还款期数-1])
            // (1+期利率)^总期数
            BigDecimal tmp1 = BigDecimal.ONE.add(periodRate).pow(totalPeriods);
            // [总本金×期利率×(1+期利率)^还款期数]
            BigDecimal tmp2 = totalPrincipalAmount.multiply(periodRate).multiply(tmp1);
            // [(1+期利率)^总期数-1]
            BigDecimal tmp3 = tmp1.subtract(BigDecimal.ONE);
            // [总本金×期利率×(1+期利率)^总期数] ÷ [(1+期利率)^总期数-1]
            instalment = tmp2.divide(tmp3, DEFAULT_CURRENCY_ROUNDING_MODE);
        } else {
            // 客户免息的情况下，单位利率为0，这时候，期供金额就是未还金额÷剩余总期数
            instalment = totalPrincipalAmount.divide(BigDecimal.valueOf(totalPeriods), DEFAULT_CURRENCY_ROUNDING_MODE);
        }
        // 四舍五入保留两位小数
        instalment = BigDecimalUtils.roundCurrency(instalment);
        param.setInstalmentAmt(instalment);
        return instalment;
    }

    /**
     *
     *
     *
     * 消金做法：
     *  将不整期导致的无法严格按照等额本息分配的金额放在首期上，
     *  实现是首期还款本金=期供-1个还款周期的利息
     *  （由于放款日~首次还款日之间的时间可能大于/小于一个还款周期，这里要的是理想中的一个还款周期的利息，区段利息计算在抽象类中），
     *  首期还款总额和标准期供差很多，
     *  末期还款总额和标准期供相差无几。
     * 村行做法：
     *  将不整期导致的无法严格按照等额本息分配的金额放到末期上，
     *  实现是首期还款本金=期供-实际还款周期的利息
     *  （由于放款日~首次还款日之间的时间可能大于/小于一个还款周期，这里要的是实际中的一个还款周期的利息，区段利息计算在抽象类中），
     *  首期还款总额和标准期供一致，
     *  末期还款总额与标准期供差很多。
     * @param param
     * @return
     */
    @Override
    public BigDecimal calcInstalmentPrincipal(InstalmentCalculatorParamDTO param) {
        if (param.getInstalmentAmt() == null) {
            calcInstalmentAmount(param);
        }
        if (param.getInterestAmt() == null) {
            calcInstalmentInterest(param);
        }
        BigDecimal ret = param.getInstalmentAmt().subtract(param.getInterestAmt());
        param.setPrincipalAmt(ret);
        return ret;
    }

    @Override
    public BigDecimal calcInstalmentInterest(InstalmentCalculatorParamDTO param) {
//        BigDecimal ret = param.getPrincipalBalance().multiply(param.getPeriodRate());
        BigDecimal ret = param.getPrincipalBalance().multiply(param.getPeriodRate());
        ret = BigDecimalUtils.roundCurrency(ret);
        param.setInterestAmt(ret);
        return ret;
    }


}
